#include <wx/wxprec.h>
#include <wx/wx.h>
#include <wx/splitter.h>
#include <wx/notebook.h>
#include <wx/listctrl.h>
#include <wx/treectrl.h>
#include <wx/config.h>
#include <wx/utils.h>
#include <wx/combo.h>

#include <AtlasBase.h>
#include <AtlasCommon.h>

#include "ContentDataView.h"
#include "StructEditView.h"
#include "ListCtrlEx.h"

enum
{
	ID_SEARCH_TEXT = wxID_HIGHEST + 1,
	ID_SEARCH_BUTTON,
	ID_OBJECT_TYPE,
	ID_OBJECT_ADD,
	ID_OBJECT_REMOVE,
	ID_OBJECT_REFLUSH,
	ID_OBJECT_SAVE,
	ID_OBJECT_UNDO,
	ID_OBJECT_LIST,
	ID_OBJECT_INFO,
};

BEGIN_EVENT_TABLE(CContentDataView, wxPanel)
	EVT_COMBOBOX(ID_OBJECT_TYPE, OnTypeSelected)
	EVT_BUTTON(ID_OBJECT_ADD, CContentDataView::OnObjectAdd)
	EVT_BUTTON(ID_OBJECT_SAVE, CContentDataView::OnObjectSave)
	EVT_BUTTON(ID_OBJECT_UNDO, CContentDataView::OnObjectUndo)
	EVT_BUTTON(ID_OBJECT_REMOVE, CContentDataView::OnObjectRemove)
	EVT_BUTTON(ID_OBJECT_REFLUSH, CContentDataView::OnObjectReflush)
	EVT_LIST_ITEM_SELECTED(ID_OBJECT_LIST, CContentDataView::OnObjectActived)
	EVT_IDLE(CContentDataView::OnIdle)
END_EVENT_TABLE()

CContentDataView::CContentDataView(wxWindow* pParent) : wxPanel(pParent)
{
	m_pCurInfo = NULL;
	m_pCurData = NULL;

	wxSplitterWindow* pSplitter = ATLAS_NEW wxSplitterWindow(this);

	// left
	wxPanel* pPanel1 = ATLAS_NEW wxPanel(pSplitter);
	wxBoxSizer* pSizer01 = ATLAS_NEW wxBoxSizer(wxHORIZONTAL);
	wxBoxSizer* pSizer03 = ATLAS_NEW wxBoxSizer(wxHORIZONTAL);
	wxBoxSizer* pSizer02 = ATLAS_NEW wxBoxSizer(wxVERTICAL);
	wxBitmap bmpAdd;
	wxBitmap bmpRemove;
	wxBitmap bmpReflush;
	bmpAdd.LoadFile(_T("../wxRes/AtlasEditor/Add.png"),			wxBITMAP_TYPE_PNG);
	bmpRemove.LoadFile(_T("../wxRes/AtlasEditor/Remove.png"),	wxBITMAP_TYPE_PNG);
	bmpReflush.LoadFile(_T("../wxRes/AtlasEditor/Reflush.png"),	wxBITMAP_TYPE_PNG);
	
	wxSize comboSize(wxDefaultSize);
	comboSize.SetWidth(300);
	m_pObjectType = ATLAS_NEW wxComboBox(pPanel1, ID_OBJECT_TYPE, wxEmptyString, wxDefaultPosition, comboSize);
	m_pObjectType->SetEditable(false);

	m_pList = ATLAS_NEW CListCtrlEx(pPanel1, ID_OBJECT_LIST, wxDefaultPosition, wxDefaultSize, wxLC_REPORT|wxLC_SINGLE_SEL);
	((CListCtrlEx*)m_pList)->SetDataView(this);

	//m_pAddObject = ATLAS_NEW wxBitmapButton(pPanel1, ID_OBJECT_ADD, bmpAdd);
	pSizer01->Add(m_pObjectType, 1, wxGROW|wxALIGN_CENTER_VERTICAL|wxALL, 5);
	wxStaticText* pStaticText = ATLAS_NEW wxStaticText(pPanel1, wxID_ANY, "Object List");
	pSizer03->Add(pStaticText, 1, wxGROW|wxALIGN_CENTER_VERTICAL|wxALL, 5);
	//pSizer01->Add(m_pAddObject, 0, wxGROW|wxALIGN_CENTER_VERTICAL|wxALL, 0);

	//pSizer01->Add(ATLAS_NEW wxBitmapButton(pPanel1, ID_OBJECT_REMOVE, bmpRemove), 0, wxGROW|wxALIGN_CENTER_VERTICAL|wxALL, 0);
	//pSizer01->Add(ATLAS_NEW wxBitmapButton(pPanel1, ID_OBJECT_REFLUSH, bmpReflush), 0, wxGROW|wxALIGN_CENTER_VERTICAL|wxALL, 0);

	//pSizer02->Add(m_pObjectType, 1, wxGROW|wxALIGN_CENTER_VERTICAL);
	
	pSizer02->Add(pSizer01, 0, wxGROW|wxALIGN_CENTER_VERTICAL, 5);
	pSizer02->Add(pSizer03, 0, wxGROW|wxALIGN_CENTER_VERTICAL, 5);
	//pSizer02->Add(ATLAS_NEW wxStaticText(pPanel1, wxID_ANY, "Object List"), 0, wxGROW|wxALIGN_CENTER_VERTICAL, 5);

	pSizer02->Add(m_pList, 1, wxGROW|wxALIGN_CENTER_VERTICAL|wxALL, 5);
	pPanel1->SetSizer(pSizer02);
	
	// right
	wxPanel* pPanel2 = ATLAS_NEW wxPanel(pSplitter);
	wxBoxSizer* pSizer11 = ATLAS_NEW wxBoxSizer(wxHORIZONTAL);
	wxBoxSizer* pSizer13 = ATLAS_NEW wxBoxSizer(wxHORIZONTAL);
	wxBoxSizer* pSizer12 = ATLAS_NEW wxBoxSizer(wxVERTICAL);

	wxBitmap bmpSave;
	wxBitmap bmpUndo;
	bmpSave.LoadFile(_T("../wxRes/AtlasEditor/Save.bmp"),			wxBITMAP_TYPE_BMP);
	bmpUndo.LoadFile(_T("../wxRes/AtlasEditor/Undo.png"),			wxBITMAP_TYPE_PNG);

	m_pObjectName = ATLAS_NEW wxTextCtrl(pPanel2, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxTE_READONLY);
	m_pInfo = ATLAS_NEW CStructEditView(pPanel2, this);
	pSizer11->Add(m_pObjectName, 1, wxGROW|wxALIGN_CENTER_VERTICAL|wxALL, 0);
	pSizer11->Add(ATLAS_NEW wxBitmapButton(pPanel2, ID_OBJECT_SAVE, bmpSave), 0, wxGROW|wxALIGN_CENTER_VERTICAL|wxALL, 0);
	pSizer11->Add(ATLAS_NEW wxBitmapButton(pPanel2, ID_OBJECT_REMOVE, bmpRemove), 0, wxGROW|wxALIGN_CENTER_VERTICAL|wxALL, 0);
	pSizer11->Add(ATLAS_NEW wxBitmapButton(pPanel2, ID_OBJECT_UNDO, bmpUndo), 0, wxGROW|wxALIGN_CENTER_VERTICAL|wxALL, 0);

	//pSizer12->Add(ATLAS_NEW wxStaticText(pPanel2, wxID_ANY, "Object Edit"), 0, wxGROW|wxALIGN_CENTER_VERTICAL|wxALL, 5);

	m_pSearchText = ATLAS_NEW wxTextCtrl(pPanel2, ID_SEARCH_TEXT, wxT(""), wxDefaultPosition, wxDefaultSize, 0);
	wxButton* pSearchBtn = ATLAS_NEW wxButton(pPanel2, ID_SEARCH_BUTTON, _("Search"));
	pSizer13->Add(m_pSearchText, 1, wxGROW|wxALIGN_LEFT|wxALL, 0);
	pSizer13->Add(pSearchBtn, 0, wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL|wxALL, 0);

	pSizer12->Add(pSizer13, 0, wxGROW|wxALIGN_CENTER_VERTICAL, 5);
	pSizer12->Add(pSizer11, 0, wxGROW|wxALIGN_CENTER_VERTICAL, 5);
	pSizer12->Add(m_pInfo, 1, wxGROW|wxALIGN_CENTER_VERTICAL, 5);

	pPanel2->SetSizer(pSizer12);

	// top
	//wxBoxSizer* pSizer1 = ATLAS_NEW wxBoxSizer(wxHORIZONTAL);
	//m_pSearchText = ATLAS_NEW wxTextCtrl(this, ID_SEARCH_TEXT, wxT(""), wxDefaultPosition, wxDefaultSize, 0);
	//pSizer1->Add(m_pSearchText, 1, wxGROW|wxALIGN_LEFT|wxALL, 5);
	//pSizer1->Add(pSearchBtn, 0, wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL|wxALL, 5);

	// main
	pSplitter->SplitVertically(pPanel1, pPanel2, 300);
	wxBoxSizer* pSizer2 = ATLAS_NEW wxBoxSizer(wxVERTICAL);
	//pSizer2->Add(pSizer1, 0, wxGROW|wxALIGN_CENTER_VERTICAL);
	pSizer2->Add(pSplitter, 1, wxGROW|wxALIGN_CENTER_VERTICAL);
	SetSizer(pSizer2);

	std::vector<const DDLReflect::STRUCT_INFO*> list;
	Atlas::ContentObject::GetTypeList(list);
	std::vector<const DDLReflect::STRUCT_INFO*>::iterator i;
	for(i=list.begin(); i!=list.end(); i++)
	{
		const DDLReflect::STRUCT_INFO* p = *i;
		while(p)
		{
			if(m_mapTypes.find(p->name)==m_mapTypes.end())
			{
				m_mapTypes[p->name] = p;
			}
			p = p->parent;
		}
	}
	std::map<std::string, const DDLReflect::STRUCT_INFO*>::iterator in;
	int nTypeIndex = -1;
	for(in=m_mapTypes.begin(); in!=m_mapTypes.end(); in++)
	{
		if(in->first=="A_CONTENT_OBJECT")
		{
			nTypeIndex = m_pObjectType->Insert(in->first.c_str(), m_pObjectType->GetCount());
		}
		else
		{
			m_pObjectType->Insert(in->first.c_str(), m_pObjectType->GetCount());
		}
		
		
	}
	m_pObjectType->SetSelection(nTypeIndex);

	m_pList->InsertColumn(0, "Name", 0, 80);
	m_pList->InsertColumn(1, "UUID", 0, 300);
	m_nSelectIndex = -1;

	FlashList();

	m_bPendingActive = false;
}

CContentDataView::~CContentDataView()
{
}

void CContentDataView::OnTypeSelected(wxCommandEvent& event)
{
	FlashList();
}

void CContentDataView::OnObjectActived(wxListEvent& event)
{
	m_pInfo->UpdateGrid();

	long i = event.GetIndex();
	if(i==-1) 
	{
		wxMessageBox(_("no select item"));
		event.Skip();
		return;
	}

	if(m_nSelectIndex != -1)
	{
		if(m_nSelectIndex == i)
		{
			event.Skip();
			return;
		}

		// check invalidate value
		wxString strInfo;
		if(!ValidateValue(strInfo) && m_nSelectIndex != i && m_bPendingActive == false)
		{
			event.Skip();
			wxLogDebug("CContentDataView::OnObjectActived(wxListEvent& event)");
			wxMessageBox(strInfo);
			m_bPendingActive = true;
			return;	
		}
	}
	
	if(m_nSelectIndex != i)
	{
		if(m_pCurInfo && m_pCurData && m_pInfo->IsModify() && m_pInfo->IsDiffItem())
		{
			if(wxMessageBox("object has been modify, save?", "!!!", wxYES_NO)==wxYES)
			{
				A_CONTENT_OBJECT* data = Atlas::ContentObject::Modify(m_pCurData->uuid, m_pCurInfo);
				if(m_pInfo->Get(m_pCurInfo, data))
				{
					m_pCurInfo = NULL;
					m_pCurData = NULL;
				
				}
				else
				{
					wxMessageBox("invalid data");
				}
			}
			else
			{
				void * pCopyData = m_pInfo->Undo();
				memcpy((void*)m_pCurData, pCopyData, m_pCurInfo->size);

				const DDLReflect::STRUCT_INFO* info = Atlas::ContentObject::GetType(m_pCurData->uuid);
				const A_CONTENT_OBJECT* object = Atlas::ContentObject::Query(m_pCurData->uuid, info);
				wxString strValue = object->name._Value;
				A_UUID uuid = m_pCurData->uuid;
				UpdateItemName(strValue, uuid);
				
				i = m_nSelectIndex;
				m_bPendingActive = true;
			}
		}
	}

	m_nSelectIndex = i;

	wxUIntPtr pData = m_pList->GetItemData(i);
	A_UUID& uuid = *(A_UUID*)pData;
	const DDLReflect::STRUCT_INFO* info = Atlas::ContentObject::GetType(uuid);
	const A_CONTENT_OBJECT* object = Atlas::ContentObject::Query(uuid, info);
	SetCurrentObject(info, object);
	//event.Skip();
}

void CContentDataView::OnObjectAdd(wxCommandEvent& event)
{
	wxString strInfo;
	m_pInfo->UpdateGrid();
	if(!ValidateValue(strInfo))
	{
		wxMessageBox(strInfo);
		return;
	}

	const DDLReflect::STRUCT_INFO* info;
	info = Atlas::ContentObject::GetType(m_pObjectType->GetValue());
	wxString strValue;
	if(!info)
	{
		wxMessageBox("invalid content class");
		return;
	}
	else
	{
		wxTextEntryDialog Dialog(this, _("Input a Name"), _("Please enter a string"));
		do
		{
			int ret = Dialog.ShowModal();
			if(ret == wxID_OK)
			{
				strValue = Dialog.GetValue();
				if(strValue.empty())
				{
					wxMessageBox("invalid empty name");
					//return;
					continue;
				}

				break;
			}
			else
			{
				return;
			}
			
		}while(true);
	}

	A_UUID uuid;
	A_CONTENT_OBJECT* object = Atlas::ContentObject::Create(info, uuid);
	if(object)
	{
		memset(object, 0, info->size);
		object->uuid = uuid;
		
		wxString strInfo;
		if(!ValidateValue(strInfo))
		{
			wxMessageBox(strInfo);
			return;
		}

		memcpy(object->name._Value, strValue.c_str(), strValue.Length());
		AppendObject(info, object);
		SelectObject(uuid);
		SetCurrentObject(info, object);
	}
}

void CContentDataView::OnObjectSave(wxCommandEvent& event)
{
	if(!m_pCurInfo || !m_pCurData) return;

	wxString strInfo;
	m_pInfo->UpdateGrid();
	if(!ValidateValue(strInfo))
	{
		wxMessageBox(strInfo);
		return;
	}

	A_CONTENT_OBJECT* data = Atlas::ContentObject::Modify(m_pCurData->uuid, m_pCurInfo);
	if(!m_pInfo->Get(m_pCurInfo, data))
	{
		wxMessageBox("invalid data or no select item");
	}
}

void CContentDataView::OnObjectUndo(wxCommandEvent& event)
{
	if(!m_pCurInfo || !m_pCurData) return;
	memcpy((void*)m_pCurData, m_pInfo->Undo(), m_pCurInfo->size);
	m_pInfo->Set(m_pCurInfo, m_pCurData);
	FlashList();
	SetCurrentObject(m_pCurInfo, m_pCurData);
}

void CContentDataView::OnObjectRemove(wxCommandEvent& event)
{
	if(m_nSelectIndex == -1 || m_pList->GetItemCount() == 0)
	{
		wxMessageBox(_("no selection"));
		return;
	}

	wxUIntPtr pData = m_pList->GetItemData(m_nSelectIndex);
	A_UUID& uuid = *(A_UUID*)pData;
	RemoveObject(uuid);

	if(m_pList->GetItemCount())
	{
		if(m_nSelectIndex >= 1)
		{
			wxUIntPtr pData = m_pList->GetItemData(m_nSelectIndex-1);
			A_UUID& uuid = *(A_UUID*)pData;
			SelectObject(uuid);
		}
		else
		{
			wxUIntPtr pData = m_pList->GetItemData(0);
			A_UUID& uuid = *(A_UUID*)pData;
			SelectObject(uuid);
		}
	}
}

void CContentDataView::OnObjectReflush(wxCommandEvent& event)
{
	//FlashList();

	if(m_pCurInfo && m_pCurData)
	{
		SelectObject(m_pCurData->uuid);
	}
}

void CContentDataView::OnIdle(wxIdleEvent& event)
{
	if(m_bPendingActive)
	{
		wxLogDebug(_("m_bPendingActive = true"));
		m_pList->SetItemState(m_nSelectIndex, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
		m_bPendingActive = false;
	}
}

void CContentDataView::OnMenu(wxWindowID id)
{
	switch(id)
	{
	case wxID_NEW:
		{
			wxCommandEvent event;
			OnObjectAdd(event);
		}
		break;
	case wxID_COPY:
		{

		}
		break;
	case wxID_PASTE:
		{

		}
		break;
	case wxID_DELETE:
		{
			wxCommandEvent event;
			OnObjectRemove(event);
		}
		break;
	case wxID_SELECTALL:
		{

		}
		break;
	case wxID_SAVE:
		{

		}
		break;
	default:
		wxLogDebug(_("ERROR MENU CMD"));
	}

}

void CContentDataView::UpdateItemName(wxString& strItemValue, A_UUID& uuid)
{
	for(long i = 0; i < m_pList->GetItemCount(); ++i)
	{
		wxUIntPtr pData = m_pList->GetItemData(i);
		A_UUID& item_uuid = *(A_UUID*)pData;
		if(item_uuid == uuid)
		{
			m_pList->SetItem(i, 0, strItemValue);
		}
	}
}

void CContentDataView::SetDebugModel()
{
	m_pInfo->SetDebugModel();
}

void CContentDataView::AppendObject(const DDLReflect::STRUCT_INFO* info, const A_CONTENT_OBJECT* data)
{
	char name[1000];
	sprintf(name, "%s %s UUID:", info->name, data->name._Value);
	AUuidToString(data->uuid, name+strlen(name));
	wxString strName = data->name._Value;
	if(strName.empty())
	{
		strName = _("NULL");
	}

	int n = m_pList->InsertItem(m_pList->GetItemCount(), strName);

	char szUUID[100];
	AUuidToString(data->uuid, szUUID);
	m_pList->SetItem(n, 1, szUUID);
	m_pList->SetItemPtrData(n, (wxUIntPtr)&data->uuid);
}

void CContentDataView::RemoveObject(const A_UUID& uuid)
{
	for(long i = 0; i < m_pList->GetItemCount(); ++i)
	{
		wxUIntPtr pData = m_pList->GetItemData(i);
		A_UUID& item_uuid = *(A_UUID*)pData;
		if(item_uuid == uuid)
		{
			m_pList->DeleteItem(i);
		}
	}
	
	Atlas::ContentObject::Delete(uuid);
	m_pCurInfo = NULL;
	m_pCurData = NULL;
	m_pInfo->Clear();
	m_pObjectName->SetValue("");
}

void CContentDataView::SelectObject(const A_UUID& uuid)
{
	for(long i = 0; i < m_pList->GetItemCount(); ++i)
	{
		wxUIntPtr pData = m_pList->GetItemData(i);
		A_UUID& item_uuid = *(A_UUID*)pData;
		if(item_uuid == uuid)
		{
			m_pList->SetItemState(i, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
		}
	}
}

void CContentDataView::SetCurrentObject(const DDLReflect::STRUCT_INFO* info, const A_CONTENT_OBJECT* data)
{
	char name[1000];
	sprintf(name, "%s %s UUID:", info->name, data->name._Value);
	AUuidToString(data->uuid, name+strlen(name));
	m_pObjectName->SetLabel(name);
	m_pInfo->Set(info, data);
	m_pCurInfo = info;
	m_pCurData = data;
	
	m_pInfo->InitPropGrid(m_pCurInfo, m_pCurData);
}

void CContentDataView::FlashList()
{
	m_pList->DeleteAllItems();

	const DDLReflect::STRUCT_INFO* info = m_mapTypes[m_pObjectType->GetValue().c_str()];
	std::vector<A_UUID> list;
	Atlas::ContentObject::GetList(info, list);

	std::vector<A_UUID>::iterator i;
	for(i=list.begin(); i!=list.end(); i++)
	{
		const A_CONTENT_OBJECT* object = Atlas::ContentObject::Query(*i);
		const DDLReflect::STRUCT_INFO* info = Atlas::ContentObject::GetType(*i);
		if(!object || !info) continue;

		AppendObject(info, object);
	}

	//m_pAddObject->Enable(Atlas::ContentObject::GetType(info->name)!=NULL);

	m_pObjectName->SetValue("");
	m_pInfo->Clear();
}

bool CContentDataView::ValidateValue(wxString& strInfo)
{
	wxArrayString strNullStringArray;
	m_pInfo->ValidateNullValue(strNullStringArray);
	if(!strNullStringArray.empty())
	{
		strInfo = _T("invalidate null value : ");
		for(wxArrayString::iterator it = strNullStringArray.begin(); it != strNullStringArray.end();)
		{
			strInfo += *it;
			++it;
			if(it == strNullStringArray.end())
			{
				break;
			}
			else
			{
				strInfo +=_(",");
			}
		}

		return false;
	}
	else
	{
		return true;
	}

}